﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
using System.IO;

namespace TUM.Mustertexte_Parser
{
    public class TextTemplate
    {
        /// <summary>
        /// Gets the name of the text template.
        /// </summary>
        public string Name
        {
            get
            {
                if (_Name == null)
                {
                    XmlNode nameNode = TextTemplateNode.SelectSingleNode("Name");
                    _Name = nameNode != null ? nameNode.InnerText : string.Empty;
                }
                return _Name;
            }
        }
        string _Name;

        /// <summary>
        /// Gets a boolean value indicating whether this is the default text template.
        /// </summary>
        public bool Default
        {
            get
            {
                if (!_Default.HasValue)
                {
                    XmlNode defaultNode = TextTemplateNode.SelectSingleNode("Standardauswahl");
                    _Default = defaultNode != null ? defaultNode.InnerText.Equals("ja", StringComparison.OrdinalIgnoreCase) : false;
                }
                return _Default.Value;
            }
        }
        bool? _Default;

        /// <summary>
        /// Gets the text template.
        /// </summary>
        public string Text
        {
            get
            {
                if (_Text == null)
                {
                    XmlNode textNode = TextTemplateNode.SelectSingleNode("Text");
                    if (textNode != null)
                    {
                        _Text = textNode.InnerXml;
                        if (_Text.StartsWith("<![CDATA[")) //Remove surrounding CDATA
                            _Text = _Text.Substring(9, _Text.Length - 12);
                    }
                    else
                        _Text = string.Empty;
                }
                return _Text;
            }
        }
        string _Text;

        /// <summary>
        /// Gets the text template in parsed form.
        /// </summary>
        public TemplateElement ParsedText
        {
            get
            {
                if (_ParsedText == null)
                {
                    XmlDocument doc = new XmlDocument();
                    try
                    {
                        doc.LoadXml("<root>" + Text + "</root>");
                    }
                    catch (Exception)
                    {
                        _ParsedText = new TemplateParagraph(new[] { new TemplateText(Text) });
                        return _ParsedText;
                    }

                    List<TemplateElement> parsedElements = new List<TemplateElement>();
                    foreach (XmlNode curParseNode in doc.FirstChild.ChildNodes)
                        parsedElements.Add(ParseNode(curParseNode));
                    _ParsedText = new TemplateElement(parsedElements.ToArray());
                }
                return _ParsedText;
            }
        }
        private TemplateElement _ParsedText;


        XmlNode TextTemplateNode;
        string TemplateFileDirectory;


        public TextTemplate(string templateFilePath, XmlNode templateNode) //Public to use the TextTemplate class from PET and other projects
        {
            if (templateNode == null)
                throw new ArgumentNullException("templateNode");

            TextTemplateNode = templateNode;

            if (templateFilePath != null)
            {
                FileInfo templateFileInfo = new FileInfo(templateFilePath);
                TemplateFileDirectory = templateFileInfo.DirectoryName;
            }
        }

        //private static Regex ParseMustertextRegex = new Regex(@"((?<before>.*?)\<(?<tag>\w+?).*?\>(?<content>.*?)\</\3\>)+(?<after>.*)", RegexOptions.Compiled);
        private TemplateElement ParseNode(XmlNode parseNode)
        {
            if(parseNode is XmlText)
            {
                return new TemplateText(HttpUtility.HtmlDecode(parseNode.InnerText));
            }

            if(parseNode is XmlElement)
            {
                List<TemplateElement> contentElements = new List<TemplateElement>();
                foreach (XmlNode curChildNode in parseNode.ChildNodes)
                    contentElements.Add(ParseNode(curChildNode));

                switch (parseNode.Name)
                {
                    case "p":
                        return new TemplateParagraph(contentElements.ToArray());

                        //Bullet list
                    case "ul":
                        return new TemplateBulletList(FilterTemplateElements<TemplateListItem>(contentElements));

                        //Numbered list
                    case "ol":
                        return new TemplateNumberedList(FilterTemplateElements<TemplateListItem>(contentElements));

                        //List item
                    case "li":
                        return new TemplateListItem(contentElements.ToArray());

                    //Bold text
                    case "b":
                        return new TemplateBold(contentElements.ToArray());

                    //Italic text
                    case "i":
                        return new TemplateItalic(contentElements.ToArray());

                        //Table
                    case "table":
                        return new TemplateTable(FilterTemplateElements<TemplateTableRow>(contentElements));

                        //Row
                    case "tr":
                        return new TemplateTableRow(FilterTemplateElements<TemplateTableCell>(contentElements.ToArray()));

                    case "td":
                    case "th":
                        return new TemplateTableCell(new TemplateElement(contentElements.ToArray()));

                    case "img":
                        {
                            var oldWorkingDir = Environment.CurrentDirectory;
                            Environment.CurrentDirectory = TemplateFileDirectory;

                            XmlNode srcAttribute = parseNode.Attributes.GetNamedItem("src");
                            if (srcAttribute != null)
                            {
                                XmlNode widthAttribute = parseNode.Attributes.GetNamedItem("width");
                                XmlNode heightAttribute = parseNode.Attributes.GetNamedItem("height");
                                int width = 0, height = 0;

                                FileInfo fi = new FileInfo(srcAttribute.Value); //Get absolute path from relative
                                Environment.CurrentDirectory = oldWorkingDir;

                                if (widthAttribute != null || heightAttribute != null)
                                {
                                    if (widthAttribute != null)
                                        int.TryParse(widthAttribute.Value, out width);
                                    if (heightAttribute != null)
                                        int.TryParse(heightAttribute.Value, out height);
                                    return new TemplateImage(fi.FullName, width, height);
                                }
                                else
                                    return new TemplateImage(fi.FullName);
                            }
                            else
                                return new TemplateImage(string.Empty);
                        }

                    default:
                        Debug.WriteLine("Unknown tag: " + parseNode.Name);
                        return new TemplateElement(contentElements.ToArray());
                }
            }
            throw new Exception("Unknown node type '" + parseNode.GetType() + "'.");
        }

        /// <summary>
        /// Filters the passed enumeration and returns only elements of the wanted type.
        /// </summary>
        private static T[] FilterTemplateElements<T>(IEnumerable<TemplateElement> elements) where T:TemplateElement
        {
            var filteredList = new List<T>();
            foreach (TemplateElement curElement in elements)
            {
                if (curElement is T)
                    filteredList.Add((T)curElement);
            }
            return filteredList.ToArray();
        }
    }
}
